package task2_2;

import task2_2.com.at.count.Function;
import task2_2.com.at.count.polsk.CounterException;
import task2_2.com.at.count.Counter;

import java.math.BigDecimal;
import java.util.List;
import java.util.ArrayList;
import java.util.Collections;
import java.io.RandomAccessFile;
import java.io.FileNotFoundException;
import java.io.IOException;

/**
 * Created by IntelliJ IDEA.
 * User: ATroshanin
 * Date: 01.06.2006
 * Time: 16:05:12
 * To change this template use File | Settings | File Templates.
 *
 *
 * ������, � ����� ��� ��������� -
 * 8/(3-8/3) = 24
 *
 */
public class MainCl {

    public static void main(String []args) {


        long start = System.currentTimeMillis();
        long grammarLoaded = 0;

        try {
            Counter counter = new Counter();
            counter.setGrammar("grammar.xml");

            grammarLoaded = System.currentTimeMillis();


            // TODO: ��������� ���� ����� ���� ����� �������� ��������
//            counter.addFunction(Function.DOUBLE_TYPE, "a1(x)", "x+5");
//            System.out.println(System.currentTimeMillis() - grammarLoaded);

//            counter.addFunction(Function.DOUBLE_TYPE, "a2(x)", "a1(x + 5)+5");
//            System.out.println(System.currentTimeMillis() - grammarLoaded);

//            counter.addParameter("s");
            //counter.setExpression("s+6*sin(48 - 5)-9/3*a2(4)");
//            counter.setExpression("(5*6+2)+2/(9-1)");
            counter.setExpression("(5*6+2)/9 + 1/5 + (4 - 6)*8");
            BigDecimal resultDub = counter.count();

            System.out.println(resultDub);

            System.exit(5556);
            counter.setExpression("-5+-9*-3+-a2(5)");

            long polsk = System.currentTimeMillis();
            counter.generateInvertedPolNotayion();
            System.out.println("Polsk = " + (System.currentTimeMillis() - polsk));

            StringBuffer strBuf = new StringBuffer();
            long counting = System.currentTimeMillis();

            for (double i=0; i < 100; i++) {
                counter.setParameter("s", i);
                double res = counter.count().doubleValue();
                double res2 = 0;
                res2 = i+6*((48 - 5) + 10)-9/3*(4+5+5);
                res2++;
                /*strBuf.append(res);
                strBuf.append("\n");*/
            }
            long countXXX = (System.currentTimeMillis() - counting);

            System.out.println("Result = " + strBuf);
            System.out.println("--------------------------");
            System.out.println("Counting XXX = " + countXXX);


            counter.setParameter("s", 5);
            System.out.println(counter.count());

            counter.setParameter("s", 15);
            System.out.println(counter.count());
        } catch (CounterException e) {
            e.printStackTrace();
        }

        //System.out.println(System.currentTimeMillis() - start);
        long time = System.currentTimeMillis() - grammarLoaded;
        System.out.println(time);

        System.exit(568);

        //System.out.println(System.currentTimeMillis() - grammarLoaded);
        /*String str11 = "q";

        boolean res11;

        res11 = str11.matches("[a-zA-Z][[a-zA-Z][0-9]]*");

        Function func;
        try {
            func = new Function(Function.INTEGER_TYPE, "sinus( x1 , x2, w)", "x1+x2+w");
            int a;
            a = 5;
        } catch (CounterException e) {
            e.printStackTrace();

        }           */
        System.exit(0);

        String curVurag = "((88-(((5))+3)*3/6))";

        double val = countVirag(curVurag);

        //TaskParser taskParser = new TaskParser("a21  sin   6+ (   (88+(((5))+3)*3/6))  ");
        TaskParser taskParser = new TaskParser("6 + 3 * 2 * sin(56 + 10/5)  ");

        List res = taskParser.parse();
        List res2 = taskParser.parse2();

        if (res2 != null) {
            TaskCounter counter = new TaskCounter(res2);
            List polsk = counter.polsk2();
            int a;
            a = 5;
        }

        GramarTest gramarTest = null;
        try {
            gramarTest = new GramarTest("grammar.xml");
            System.out.println(gramarTest.test(res));
        } catch (GrammarException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        }

        if (res != null) {
            TaskCounter counter = new TaskCounter(res);
            List polskZap = counter.Polsk();
            if (polskZap == null) {
                System.out.println("NULL");
            } else {
                for (int i=0; i<polskZap.size(); i++) {
                    Lexem lex = (Lexem) polskZap.get(i);
                    System.out.println(lex.getValue());
                }
            }
        }

        //-----------------------------------

        boolean generate = false;
        boolean calc = false;

        if (generate) {
            generateVirags();
        } else if (calc){
            calculate();
        }

        double x;
        x = 8.0/(3.0-8.0/3.0);

        System.out.println(x);

    }

    private static void calculate() {
        RandomAccessFile rf = null;
        String virag24 = "";
        try {
            rf = new RandomAccessFile("logs/virag.log", "rw");

            String curStr;

            do {
                //rf.seek(0);
                curStr = rf.readLine();

                if (curStr != null) {

                    double res = countVirag(curStr);

                    if (res >23.8 && res < 24.2) {
                        virag24 = curStr;
                        System.out.println("*****************************");
                        System.out.println("YPA");
                        System.out.println("*****************************");
                        System.out.println("Virag = " + curStr + "; res = " + res);
                        System.out.println("*****************************");
                        System.out.println("*****************************");
                    } else {
                        System.out.println("Virag = " + curStr + "; res = " + res);
                    }

                }

            } while (curStr != null);


            rf.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        } catch (IOException e) {
            e.printStackTrace();
        }

        System.out.println("*****************************");
        System.out.println("YPA");
        System.out.println("*****************************");
        System.out.println("Virag = " + virag24 + "; res = " + 24);
        System.out.println("*****************************");
        System.out.println("*****************************");


    }

    private static double countVirag(String curVurag) {

        try {
            TaskParser taskParser = new TaskParser(curVurag);

            List res = taskParser.parse();

            if (res != null) {
                TaskCounter counter = new TaskCounter(res);
                List polskZap = counter.Polsk();
                if (polskZap == null) {
                    return 0;
                } else {
                    for (int k=0; k<polskZap.size(); k++) {
                        Lexem lex = (Lexem) polskZap.get(k);
                        lex.setDouleValue();
                    }
                    int i = 0;
                    while (polskZap.size() > 1) {
                        Lexem lex = (Lexem) polskZap.get(i);

                        if (lex.getType().equals(Lexem.TYPE_ZNAK)) {
                            Lexem one = (Lexem) polskZap.get(i - 2);
                            Lexem two = (Lexem) polskZap.get(i - 1);
                            double d_one = one.getDouleValue();
                            double d_two = two.getDouleValue();
                            if (lex.getValue().equals("+")) {
                                one.setDouleValue(d_one + d_two);
                            } else if (lex.getValue().equals("-")) {
                                one.setDouleValue(d_one - d_two);
                            } else if (lex.getValue().equals("*")) {
                                one.setDouleValue(d_one * d_two);
                            } else if (lex.getValue().equals("/")) {
                                one.setDouleValue(d_one / d_two);
                            }
                            polskZap.remove(i);
                            polskZap.remove(i-1);
                            i = 0;
                        } else {
                            i++;
                        }
                    }
                    return ((Lexem)polskZap.get(0)).getDouleValue();

                }
            }
        } catch (Exception e) {
            return 99999.00;
        }

        return 0;
    }




    private static void generateVirags() {
        List availableSymbols = new ArrayList();
        TaskSymbol sym = new TaskSymbol('3', true);
        availableSymbols.add(sym);
        sym = new TaskSymbol('3', true);
        availableSymbols.add(sym);
        sym = new TaskSymbol('8', true);
        availableSymbols.add(sym);
        sym = new TaskSymbol('8', true);
        availableSymbols.add(sym);
        sym = new TaskSymbol('(', false);
        availableSymbols.add(sym);
        sym = new TaskSymbol(')', false);
        availableSymbols.add(sym);
        sym = new TaskSymbol('+', false);
        availableSymbols.add(sym);
        sym = new TaskSymbol('-', false);
        availableSymbols.add(sym);
        sym = new TaskSymbol('*', false);
        availableSymbols.add(sym);
        sym = new TaskSymbol('/', false);
        availableSymbols.add(sym);


        int strSize = 9;
        List result = new ArrayList();
        System.out.println("--------------------------------");
        while ( strSize > 5)
        {
            int curStrSize = strSize;

            for (int i=0; i< availableSymbols.size(); i++) {

                TaskSymbol taskSym = (TaskSymbol) availableSymbols.get(i);
                taskSym.setTimeUsedCount(1);
                String strNach = "" + taskSym.getSymbol();

                List strsAfterNach = getStrsAfterNach(availableSymbols, curStrSize - 1);

                if (taskSym.isZnak()) {
                    continue;
                }

                // TODO:

                for (int k=0; k<strsAfterNach.size(); k++) {
                    String strAfterNach = (String) strsAfterNach.get(k);


                    if (strAfterNachCanBeAdded(taskSym, strNach, strAfterNach)) {
                        if (checkScobcas(strNach + strAfterNach)) {
                            if (hasAllDigits(strNach + strAfterNach)) {
                                result.add(strNach + strAfterNach);
                            }
                        }
                    }
                }


                int a;
                a = 5;
                Collections.sort(result);
                removeDoublesFromList(result);

                taskSym.setTimeUsedCount(0);
                System.out.println(result.size());
            }

            strSize--;
        }

        RandomAccessFile rf = null;
        try {
            rf = new RandomAccessFile("logs/virag.log", "rw");

            for (int i=0; i<result.size(); i++) {
                String str_ = ((String) result.get(i)) + "\n";

                rf.write(str_.getBytes());
            }
            rf.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static boolean hasAllDigits(String s) {
        char[] syms = s.toCharArray();
        int count3 = 0;
        int count8 = 0;
        for (int i=0; i<syms.length; i++) {
            if (syms[i] == '3') {
                count3++;
            }
            if (syms[i] == '8') {
                count8++;
            }
        }
        if (count3 != 2) {
            return false;
        }
        if (count8 != 2) {
            return false;
        }
        return true;
    }

    private static boolean checkScobcas(String s) {
        char[] syms = s.toCharArray();
        int sc1Count = 0;
        int sc2Count = 0;
        for (int i=0; i<syms.length; i++) {
            if (syms[i] == '(') {
                sc1Count++;
            }
            if (syms[i] == ')') {
                sc2Count++;
            }
        }
        if (sc1Count != sc2Count) {
            return false;
        }
        return true;
    }

    private static List getStrsAfterNach(List availableSymbols, int curStrSize) {

        List result = new ArrayList();

        if (curStrSize == 1) {
            for (int i=0; i< availableSymbols.size(); i++) {
                TaskSymbol taskSym = (TaskSymbol) availableSymbols.get(i);

                if (taskSym.isUseOneTime() && taskSym.getTimeUsedCount() > 0) {
                    continue;
                }

                if (taskSym.isZnak()) {
                    continue;
                }

                if (taskSym.getSymbol() == '(') {
                    continue;
                }

                taskSym.setTimeUsedCount(1);
                String strNach = "" + taskSym.getSymbol();
                result.add(strNach);

                taskSym.setTimeUsedCount(0);
            }
        } else {
            for (int i=0; i< availableSymbols.size(); i++) {

                TaskSymbol taskSym = (TaskSymbol) availableSymbols.get(i);

                if (taskSym.isUseOneTime() && taskSym.getTimeUsedCount() > 0) {
                    continue;
                }
                if (noDigit(availableSymbols) && taskSym.isZnak()) {
                    continue;
                }

                taskSym.setTimeUsedCount(1);
                String strNach = "" + taskSym.getSymbol();

                List strsAfterNach = getStrsAfterNach(availableSymbols, curStrSize - 1);

                Collections.sort(strsAfterNach);
                removeDoublesFromList(strsAfterNach);

                for (int k=0; k<strsAfterNach.size(); k++) {
                    String curStrAfterNach = (String) strsAfterNach.get(k);

                    /*char q = curStrAfterNach.charAt(0);
                    if (taskSym.isZnak() && (q == '+' || q == '-' || q == '/' || q == '*')) {
                        continue;
                    }

                    if (taskSym.isZnak() && q == ')') {
                        continue;
                    }

                    if (taskSym.getSymbol() == ')' && q == '(') {
                        continue;
                    }
                    if (taskSym.getSymbol() == ')' && (q >= 48 && q <= 57) ) {
                        continue;
                    }

                    if (taskSym.isDigit() && q == '(') {
                        continue;
                    }

                    if (taskSym.getSymbol() == '(' && q == ')') {
                        continue;
                    }
                    if (taskSym.getSymbol() == '(' && (q == '+' || q == '-' || q == '/' || q == '*')) {
                        continue;
                    }

                    if (hasToMachScobca(strNach + curStrAfterNach)) {
                        continue;
                    }

                    if (scobLeftMoreScobRight(strNach + curStrAfterNach)) {
                        continue;
                    }*/

                    if (strAfterNachCanBeAdded(taskSym, strNach, curStrAfterNach)) {
                        result.add(strNach + curStrAfterNach);
                    }

                }

                taskSym.setTimeUsedCount(0);
            }
        }

        int a;
        a = 5;

        return result;
    }

    private static boolean scobLeftMoreScobRight(String s) {
        char[] syms = s.toCharArray();
        int sc1Count = 0;
        int sc2Count = 0;
        for (int i=0; i<syms.length; i++) {
            if (syms[i] == '(') {
                sc1Count++;
            }
            if (syms[i] == ')') {
                sc2Count++;
            }
        }
        if (sc1Count > sc2Count) {
            return true;
        }
        return false;
    }

    private static boolean hasToMachScobca(String s) {
        char[] syms = s.toCharArray();
        int sc1Count = 0;
        int sc2Count = 0;
        for (int i=0; i<syms.length - 1; i++) {
            if (syms[i] == '(' && syms[i+1] == '(') {
                sc1Count++;
                if (sc1Count == 2) {
                    return true;
                }
            } else {
                sc1Count = 0;
            }
            if (syms[i] == ')' && syms[i+1] == ')') {
                sc2Count++;
                if (sc2Count == 2) {
                    return true;
                }
            } else {
                sc2Count = 0;
            }
        }

        return false;
    }

    private static boolean noDigit(List availableSymbols) {

        for (int i=0; i< availableSymbols.size(); i++) {
            TaskSymbol taskSym = (TaskSymbol) availableSymbols.get(i);

            if (taskSym.isUseOneTime()) {

                if (taskSym.getTimeUsedCount() == 0) {
                    return false;
                }

            }
        }


        return true;
    }

    private static boolean strAfterNachCanBeAdded(TaskSymbol taskSym, String strNach, String curStrAfterNach) {
        char q = curStrAfterNach.charAt(0);
        if (taskSym.isZnak() && (q == '+' || q == '-' || q == '/' || q == '*')) {
            return false;
        }

        if (taskSym.isZnak() && q == ')') {
            return false;
        }

        if (taskSym.getSymbol() == ')' && q == '(') {
            return false;
        }
        if (taskSym.getSymbol() == ')' && (q >= 48 && q <= 57) ) {
            return false;
        }

        if (taskSym.isDigit() && q == '(') {
            return false;
        }

        if (taskSym.getSymbol() == '(' && q == ')') {
            return false;
        }
        if (taskSym.getSymbol() == '(' && (q == '+' || q == '-' || q == '/' || q == '*')) {
            return false;
        }

        if (hasToMachScobca(strNach + curStrAfterNach)) {
            return false;
        }

        if (scobLeftMoreScobRight(strNach + curStrAfterNach)) {
            return false;
        }

        return true;
    }

    private static void removeDoublesFromList(List lst) {

        for (int i=0; i<lst.size(); i++) {

            while (i+1 != lst.size() && lst.get(i).equals(lst.get(i+1))) {
                lst.remove(i+1);
            }

        }

    }
}
